// VPErrorHelper.h

#pragma once

// Verifies that [T] is a HRESULT type.
template <typename T>
inline typename std::enable_if<std::is_same<T, HRESULT>::value, HRESULT>::type
__as_hresult(T hr) { return hr; }

// Verifies that [const T] can be converted to a const pointer
template <typename T>
inline typename std::enable_if<std::is_convertible<const T, const void*>::value, HRESULT>::type
__fail_if_null(const T& value) { return (value == nullptr) ? E_POINTER : S_OK; }

// Verifies that [T] can be converted to a const pointer
template <typename T>
inline typename std::enable_if<std::is_convertible<T, const void*>::value, HRESULT>::type
__fail_if_null(T& value) { return (value == nullptr) ? E_POINTER : S_OK; }

template <typename T>
HRESULT __fail_if_null(const std::unique_ptr<T>& value) { return value ? S_OK : E_POINTER; }

template <typename T>
HRESULT __fail_if_null(const std::shared_ptr<T>& value) { return value ? S_OK : E_POINTER; }

// Verifies that [T] can be treated as a boolean expression and is not an HRESULT
template <typename T>
inline typename std::enable_if<
    std::is_integral<T>::value &&
    !std::is_same<T, HRESULT>::value,
    HRESULT
>::type
__fail_if_false(const T& value) { return (value) ? S_OK : E_FAIL; }

#define RETURN_IF_FAILED_COPY_MSG(function, error_message)         \
    {                                                              \
        HRESULT hr = E_FAIL;                                       \
        if (FAILED(hr = __as_hresult(function)))                   \
        {                                                          \
            VPErrorHandler::WriteLog(__FUNCTION__, error_message); \
            return hr;                                             \
        }                                                          \
    }

#define __FUNCTION_LINE_COPY_COPY__(x) #x
#define __FUNCTION_LINE_COPY__(x) __FUNCTION_LINE_COPY_COPY__(x)
#define __FUNCTION_LINE__ __FUNCTION_LINE_COPY__(__LINE__)

// If call function that return HRESULT type, use this micro to return E_FAIL and record a error message.
#define RETURN_IF_FAILED_COPY(function) \
    RETURN_IF_FAILED_COPY_MSG(function, "Failed: " #function " on line " __FUNCTION_LINE__)
#define RETURN_IF_FAILED(function) \
    RETURN_IF_FAILED_COPY(function)

// If call function that return a pointer, use this micro to return E_FAIL and record a error message.
#define FAIL_IF_NULL_COPY(f) \
    RETURN_IF_FAILED_COPY_MSG(__fail_if_null(f), "Failed: " #f " was nullptr. On line " __FUNCTION_LINE__)
#define FAIL_IF_NULL(f) FAIL_IF_NULL_COPY(f)

// If call function that return a BOOL or bool value, use this micro to return E_FAIL and record a error message.
#define FAIL_IF_FALSE_COPY(f) \
    RETURN_IF_FAILED_COPY_MSG(__fail_if_false(f), "Failed: " #f " was FALSE. On line " __FUNCTION_LINE__)
#define FAIL_IF_FALSE(f) FAIL_IF_FALSE_COPY(f)
